/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 isometric_map.cpp

	$Header: /heroes4/isometric_map.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "isometric_map.h"

#include <cassert>

// -------------------------------------------------------------------
// t_isometric_map_base_base members
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// -------------------------------------------------------------------
t_isometric_map_base_base::t_isometric_map_base_base()
	:	m_size( 0 ),
		m_tile_height( 0 ),
		m_view_size( 0, 0 )
{
}

// -------------------------------------------------------------------
// convert a screen point to a map point
// -------------------------------------------------------------------
t_map_point_2d t_isometric_map_base_base::get_map_point( t_screen_point const & point ) const
{
	return convert_screen_point_to_map( point, m_tile_height, m_view_size, m_size );
}

// -------------------------------------------------------------------
// convert a map point to a screen point
// -------------------------------------------------------------------
t_screen_point t_isometric_map_base_base::get_screen_point( t_map_point_2d const & point ) const
{
	return convert_map_point_to_screen( point, m_tile_height, m_view_size, m_size );
}

// -------------------------------------------------------------------
// initialize the sparse array given a map size, tile_size, and screen size
// -------------------------------------------------------------------
void t_isometric_map_base_base::base_initialize( int size, int tile_height, t_screen_point const & view_size )
{
	m_size = size;
	m_tile_height = tile_height;
	m_view_size = view_size;
	m_level_size = 0;

	int tile_width = tile_height * 2;

	int view_top = ( m_size * m_tile_height -  m_view_size.y ) / 2;
	int view_left = -( m_size * tile_width / 2 ) + ( m_size * tile_width - m_view_size.x ) / 2;
	int view_bottom = view_top + m_view_size.y;
	int view_right = view_left + m_view_size.x;
	
	int top_edge_sum = ( view_top * 2 ) / tile_height - 1;
	int bottom_edge_sum = ( view_bottom * 2 + ( tile_height - 1 ) ) / tile_height;
	int left_edge_sum = ( view_left * 2 - ( tile_width - 1 ) ) / tile_width;
	int right_edge_sum = ( view_right * 2 + ( tile_width - 1 ) ) / tile_width + 1;

	m_row_data_ptr->resize( m_size );
	int row;
	for ( row = 0; row < m_size; ++row )
	{
		t_row_data & row_data = ( *m_row_data_ptr )[ row ];

		if ( top_edge_sum - row >= 0 )
			row_data.start = top_edge_sum - row;
		else if ( left_edge_sum + row >= 0 )
			row_data.start = left_edge_sum + row;
		else
			row_data.start = 0;

		if ( right_edge_sum + row <= m_size )
			row_data.end = right_edge_sum + row;
		else if ( bottom_edge_sum - row <= m_size )
			row_data.end = bottom_edge_sum - row;
		else
			row_data.end = m_size;

		assert( row_data.end > row_data.start );

		row_data.offset = m_level_size - row_data.start;
		m_level_size += row_data.end - row_data.start;
	}
}

// -------------------------------------------------------------------
// t_isometric_tile_map_base members
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// initialize, just knowing the desired map size, computing the view size.
// -------------------------------------------------------------------
void t_isometric_tile_map_base::initialize( int size, int tile_height )
{
	int const tile_width = tile_height * 2;

	m_view_size.x = tile_width * size / 2;
	m_view_size.y = tile_height * size / 2;
	base_initialize( size, tile_height, m_view_size );
}

// -------------------------------------------------------------------
// initialize, knowing just the view size, and compute the map size.
// -------------------------------------------------------------------
void t_isometric_tile_map_base::initialize( t_screen_point const & view_size, int tile_height )
{
	int const tile_width = tile_height * 2;

	// compute map size
	int size = ( view_size.x + 2 * view_size.y + tile_width - 1 ) / tile_width;
	base_initialize( size, tile_height, view_size );
}

// -------------------------------------------------------------------
// t_isometric_vertex_map_base members
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// initialize, just knowing the desired map size, computing the view size.
// -------------------------------------------------------------------
void t_isometric_vertex_map_base::initialize( int size, int tile_height )
{
	int const tile_width = tile_height * 2;

	m_view_size.x = tile_width * size / 2;
	m_view_size.y = tile_height * size / 2;
	initialize( size, tile_height, m_view_size );
}

// -------------------------------------------------------------------
// initialize, knowing just the view size, and compute the map size.
// -------------------------------------------------------------------
void t_isometric_vertex_map_base::initialize( t_screen_point const & view_size, int tile_height )
{
	int const tile_width = tile_height * 2;

	// compute map size
	int size = ( view_size.x + 2 * view_size.y + tile_width - 1 ) / tile_width;
	initialize( size, tile_height, view_size );
}

// -------------------------------------------------------------------
// initialize the sparse array given a map size, tile_size, and screen size
// -------------------------------------------------------------------
void t_isometric_vertex_map_base::initialize( int size, int tile_height, t_screen_point const & view_size )
{
	base_initialize( size, tile_height, view_size );
	assert( m_row_data_ptr->size() == m_size );

	// The vertex map size is one greater than the tile map size
	++m_size;

	std::vector< t_row_data > & row_data_vector = *m_row_data_ptr;
	std::vector< t_row_data > work_row_data_vector( m_size );
	work_row_data_vector.swap( row_data_vector );

	// Fixup the first row
	row_data_vector.front() = work_row_data_vector.front();
	++row_data_vector.front().end;
	m_level_size = row_data_vector.front().end - row_data_vector.front().start;

	// Fixup the middle rows
	int row;
	for ( row = 1; row < row_data_vector.size() - 1; ++row )
	{
		t_row_data & row_data = row_data_vector[ row ];
		t_row_data & this_work_row_data = work_row_data_vector[ row ];
		t_row_data & prev_work_row_data = work_row_data_vector[ row - 1 ];

		row_data.start = this_work_row_data.start < prev_work_row_data.start ? this_work_row_data.start : prev_work_row_data.start;
		row_data.end = ( this_work_row_data.end > prev_work_row_data.end ? this_work_row_data.end : prev_work_row_data.end ) + 1;
		row_data.offset = m_level_size - row_data.start;
		m_level_size += row_data.end - row_data.start;
	}

	// Fixup the last row
	row_data_vector.back().start = work_row_data_vector.back().start;
	row_data_vector.back().end = work_row_data_vector.back().end + 1;
	row_data_vector.back().offset = m_level_size - row_data_vector.back().start;
	m_level_size += row_data_vector.back().end - row_data_vector.back().start;
}
